﻿#include "httpserver.h"
#include "connection.h"
#include "buffer.h"
#include "util.h"
// 写入响应
void HttpServer::writeResponse(const ConnectionPtr &conn, const HttpRequest &req, HttpResponse &rsp)
{
    if (req.close() == true)
    {
        rsp.setHeader("Connection", "close");
    }
    else
    {
        rsp.setHeader("Connection", "keep-alive");
    }
    if (rsp.m_body.empty() == false && rsp.hasHeader("Content-Length") == false)
    {
        rsp.setHeader("Content-Length", std::to_string(rsp.m_body.size()));
    }
    if (rsp.m_body.empty() == false && rsp.hasHeader("Content-Type") == false)
    {
        rsp.setHeader("Content-Type", "application/octet-stream");
    }
    if (rsp.m_redirectFlag == true)
    {
        rsp.setHeader("Location", rsp.m_redirectUrl);
    }
    // 2. 将rsp中的要素，按照http协议格式进行组织
    std::stringstream rsp_str;
    rsp_str << req.m_version << " " << std::to_string(rsp.m_statuCode) << " " << Util::statuDes(rsp.m_statuCode) << "\r\n";
    for (auto &head : rsp.m_headers)
    {
        rsp_str << head.first << ": " << head.second << "\r\n";
    }
    rsp_str << "\r\n";
    rsp_str << rsp.m_body;
    // 3. 发送数据, 这里怎么没报错
    
    conn->send(rsp_str.str().c_str(), rsp_str.str().size());
}
// 是否是静态资源请求
bool HttpServer::isFileHandle(HttpRequest &req, HttpResponse *rsp)
{
    // 1. 必须设置了静态资源根目录
    if (m_basedic.empty())
    {
        return false;
    }
    // 2. 请求方法，必须是GET / HEAD请求方法
    if (req.m_method != "GET" && req.m_method != "HEAD")
    {
        return false;
    }
    // 3. 请求的资源路径必须是一个合法路径
    if (Util::validPath(req.m_path) == false)
    {
        return false;
    }
    // 4. 请求的资源必须存在,且是一个普通文件
    //    有一种请求比较特殊 -- 目录：/, /image/， 这种情况给后边默认追加一个 index.html
    // index.html    /image/a.png
    // 不要忘了前缀的相对根目录,也就是将请求路径转换为实际存在的路径  /image/a.png  ->   ./wwwroot/image/a.png
    std::string reqPath = m_basedic + req.m_path; // 为了避免直接修改请求的资源路径，因此定义一个临时对象
    if (req.m_path.back() == '/')
    {
        reqPath += "index.html";
    }
    if (Util::isRegular(reqPath) == false)
    {
        return false;
    }
    return true;
}
// 静态资源请求处理
void HttpServer::fileHandle(const HttpRequest &req, HttpResponse *rsp)
{
    std::string reqPath = m_basedic + req.m_path;
    if (req.m_path.back() == '/')  
    {
        reqPath += "index.html";
    }
    Buffer buf;
    bool ret = Util::readFile(reqPath, &buf);
    if (ret == false)
    {
        return;
    }
    rsp->m_body.append(buf.getReadAddr(), buf.getReadSize());
    buf.moveReadStep(buf.getReadSize());
    return;
}
// 功能请求处理
void HttpServer::funcHandle(HttpRequest &req, HttpResponse *rsp, Handlers &handlers)
{
    // 在对应请求方法的路由表中，查找是否含有对应资源请求的处理函数，有则调用，没有则发挥404
    // 思想：路由表存储的时键值对 -- 正则表达式 & 处理函数
    // 使用正则表达式，对请求的资源路径进行正则匹配，匹配成功就使用对应函数进行处理
    //   /numbers/(\d+)       /numbers/12345
    for (auto &handler : handlers)
    {
        const std::regex &e = handler.first;
        const Handler &functor = handler.second;
        bool ret = std::regex_match(req.m_path, req.m_matches, e);///这里的_matches好像没什么用
        if (ret == false)
        {
            continue;
        }
        return functor(req, rsp); // 传入请求信息，和空的rsp，执行处理函数
    }
    rsp->m_statuCode = 404;
    notFountHandle(req, rsp);
}
// 错误请求处理
void HttpServer::notFountHandle(const HttpRequest &req, HttpResponse *rsp)
{
    // 1. 返回错误网页
    Buffer buf;
    std::string body;
    if (Util::readFile("wwwroot/404.html", &buf))
    {
        body.append(buf.getReadAddr(), buf.getReadSize());
        buf.moveReadStep(buf.getReadSize());
    }
    rsp->setBody(body, "text/html");
}
void HttpServer::errorHandle(const HttpRequest &req, HttpResponse *rsp)
{
    //错误处理
}
// 路由
void HttpServer::route(HttpRequest &req, HttpResponse *rsp)
{
    // 1. 对请求进行分辨
    if (isFileHandle(req, rsp) == true)
    {
        return fileHandle(req, rsp);
    }
    if (req.m_method == "GET" || req.m_method == "HEAD")
    {
        return funcHandle(req, rsp, m_getRoute);
    }
    else if (req.m_method == "POST")
    {
        return funcHandle(req, rsp, m_postRoute);
    }
    else if (req.m_method == "PUT")
    {
        return funcHandle(req, rsp, m_putRoute);
    }
    else if (req.m_method == "DELETE")
    {
        return funcHandle(req, rsp, m_deleteRoute);
    }
    rsp->m_statuCode = 405; // Method Not Allowed
}
// 设置协议上下文 --> TcpServer::SetContext
void HttpServer::onConnected(const ConnectionPtr &conn)
{
    conn->setContext(HttpContext());
}
// 处理缓冲区和解析请求
void HttpServer::onMessage(const ConnectionPtr &conn, Buffer *buf)
{
    while (buf->getReadSize() > 0)
    {
        // 1. 获取上下文
        HttpContext *context = conn->getContext()->get<HttpContext>();
        // 2. 通过上下文对缓存区进行处理，得到HttpRequest对象
        //   2.1 如果解析失败，返回错误响应
        context->recvHttpRequest(buf);
        HttpResponse rsp(context->respStatu());
        HttpRequest &req = context->request();
        if (context->respStatu() >= 400)
        {
            errorHandle(req, &rsp);
            writeResponse(conn, req, rsp);
            context->reset();
            buf->moveReadStep(buf->getReadSize()); // 出错了就把缓冲区数据清空
            conn->shutDown();
            return;
        }
        if (context->recvStatu() != HttpRecvStatu::RECV_REQUEST_OVER)
        {
            // 数据未接受完，继续等待接受
            return;
        }
        // 3. 请求路由
        route(req, &rsp);
        // 4. 对HttpResponse进行组织发送
        writeResponse(conn, req, rsp);
        // 5. 重置上下文
        context->reset();
        // 6. 根据长短连接，绝定是否断开连接
        if (rsp.close())
        {
            conn->shutDown();
            return;
        }
    }
}

HttpServer::HttpServer(int port, int timeout, const std::string &ip) 
    : m_server(port, ip)
{
    m_server.enableInactiveRelease(timeout);
    m_server.setConnectCallback(std::bind(&HttpServer::onConnected, this, std::placeholders::_1));
    m_server.setMessageCallback(std::bind(&HttpServer::onMessage, this, std::placeholders::_1, std::placeholders::_2));
}
void HttpServer::start()
{
    return m_server.start();
}
//设置相对根目录
void HttpServer::setBaseDic(const std::string& path)
{
    assert(Util::isDirectory(path) == true);
    m_basedic = path;
}
// 设置线程池数量
void HttpServer::setThreadCount(int n)
{
    m_server.setThreadCount(n);
}
// 请求
void HttpServer::get(const std::string &pattern, const Handler &handler)
{
    m_getRoute.push_back(std::make_pair(std::regex(pattern), handler));
}
void HttpServer::post(const std::string &pattern, const Handler &handler)
{
    m_postRoute.push_back(std::make_pair(std::regex(pattern), handler));
}
void HttpServer::put(const std::string &pattern, const Handler &handler)
{
    m_putRoute.push_back(std::make_pair(std::regex(pattern), handler));
}
void HttpServer::del(const std::string &pattern, Handler &handler)
{
    m_deleteRoute.push_back(std::make_pair(std::regex(pattern), handler));
}
